<?php

namespace System\library\helper;

use System\InitSystem;
use System\library\config\Config;
use System\library\Debugger;

/**
 * <b>Class</b> :        HtmlHelper<br>
 * <b>Fonction</b> :     Un utilitaire html
 * <br>
 * @version              1.0
 * @author               Dread <dreadlokeur@gmail.com>
 * @license              Licence publique générale GNU http://www.gnu.org/licenses/gpl.html
 * @copyright            Copyright 2011 - MidichloriansPHP and contributors
 * @package              System
 * @subpackage           library\helper
 */
class HtmlHelper {

    // http://www.w3.org/QA/2002/04/valid-dtd-list.html
    private $_htmlVersionList = array(
        // Html 4
        'HTML 4.01' => 'http://www.w3.org/TR/html4/strict.dtd',
        'HTML 4.01 Transitional' => 'http://www.w3.org/TR/html4/loose.dtd',
        'HTML 4.01 Frameset' => 'http://www.w3.org/TR/html4/frameset.dtd',
        // Xhtml 1.0
        'XHTML 1.0 Strict' => 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd',
        'XHTML 1.0 Transitional' => 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd',
        'XHTML 1.0 Frameset' => 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd',
        // Xhtml 1.1
        'XHTML 1.1' => 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd',
        'XHTML Basic 1.1' => 'http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd'
            //HTML 5 not yet
    );
    private $_htmlVersion = null;
    private $_htmlData = null;
    private $_indente = true;

    private function _checkHtmlVersion($htmlVersion) {
        if (!is_string($htmlVersion))
            throw new \Exception('Html version parameter must be a string');
        if (!array_key_exists($htmlVersion, $this->_htmlVersionList))
            throw new \Exception('Html version "' . $htmlVersion . '" is invalid');
    }

    private function _selectHtmlFileData($htmlVersion) {
        switch ($htmlVersion) {
            case 'HTML 4.01':
            case 'HTML 4.01 Transitional':
            case 'HTML 4.01 Frameset':
                throw new \Exception('No Yet');
                break;
            case 'XHTML 1.0 Strict':
            case 'XHTML 1.0 Transitional':
            case 'XHTML 1.0 Frameset':
                throw new \Exception('No Yet');
                break;
            case 'XHTML 1.1':
            case 'XHTML Basic 1.1':
                $file = 'Xhtml-1.1.xml';
                break;
            default:
                throw new \Exception('Invalid Html version for loading html file data');
                break;
        }

        return Config::getCons('PATH_SYSTEM') . 'data' . DIRECTORY_SEPARATOR . 'html' . DIRECTORY_SEPARATOR . $file;
    }

    private function _loadHtmlDb($dbFile) {
        if (!file_exists($dbFile))
            throw new \Exception('"' . $dbFile . '" n\'existe pas');
        $db = @simplexml_load_file($dbFile);
        if ($db === null || $db === false)
            throw new \Exception('"' . $dbFile . '" n\'est pas un fichier XML valid');

        return $db;
    }

    public function __construct($indente = true, $htmlVersion = 'XHTML 1.1') {
        $this->setIndente($indente);
        $this->setHtmlVersion($htmlVersion);
        // Checking and loading html data
        $this->_htmlData = $this->_loadHtmlDb($this->_selectHtmlFileData($this->_htmlVersion));
    }

    public function setHtmlVersion($htmlVersion) {
        $this->_checkHtmlVersion($htmlVersion);
        $this->_htmlVersion = $htmlVersion;
    }

    public function setIndente($bool) {
        if (!is_bool($bool))
            throw new \Exception('Indente param must be a boolean');
        $this->_indente = $bool;
    }

    public function getBalise($baliseName, $attributes = array()) {
        if (!is_string($baliseName))
            throw new \Exception('Balise name must be a string');
        // On regarde que la balise demandé existe dans le xml data
        $baliseInfo = false;
        foreach ($this->_htmlData->baliseList->balise as $balise) {
            if ($balise->name == $baliseName) {
                $baliseInfo = $balise;
                break;
            }
        }
        if (!$baliseInfo)
            throw new \Exception('La balise "' . $baliseName . '" est invalid');


        // Avec les informations de la balise, on va établir une liste des attributes accéptés et requis
        if (property_exists($baliseInfo, 'acceptedAttributes'))
            $acceptedAttributes = explode(',', $baliseInfo->acceptedAttributes);
        if (property_exists($baliseInfo, 'requiredAttributes')) {
            $requiredAttributes = explode(',', $baliseInfo->requiredAttributes);
            // On regarde si l'attribut obligatoirement requis n'est pas manquant
            foreach ($requiredAttributes as $attributeRequired) {
                if (!array_key_exists($attributeRequired, $attributes))
                    throw new \Exception('L\'attribut "' . $attributeRequired . '" obligatoirement requis est manquant');
            }
        }

        // Et maintenant on traite les attributs passer en paramètres
        $attributesList = '';
        if (!empty($attributes)) {
            foreach ($attributes as $attribute => &$value) {
                // On vérifie que l'attribut est autorisé
                if (!in_array($attribute, $acceptedAttributes))
                    throw new \Exception('Attribute "' . $attribute . '" nest pas autorisé');

                // On récupère les informations de l'attribut dans le xml data
                $attributeInfo = false;
                foreach ($this->_htmlData->attributeList->attribute as $attr) {
                    if ($attr->name == $attribute) {
                        $attributeInfo = $attr;
                        break;
                    }
                }
                // On effectue quelques petites vérifications
                if ($attributeInfo) {
                    if (property_exists($attributeInfo, 'refusedBalise')) {
                        $refusedBalises = explode(',', $attributeInfo->refusedBalise);
                        if (in_array($baliseName, $refusedBalises))
                            throw new \Exception('La balise "' . $baliseName . '" n\'est pas autorisé pour cet attribut');
                    }
                    if (property_exists($attributeInfo, 'acceptedBalise')) {
                        $acceptedBalises = explode(',', $attributeInfo->acceptedBalise);
                        if (!in_array($baliseName, $acceptedBalises))
                            throw new \Exception('La balise "' . $baliseName . '" n\'est pas autorisé pour cet attribut');
                    }
                    if (property_exists($attributeInfo, 'acceptedValue')) {
                        $acceptedValue = explode(',', $attributeInfo->acceptedValue);
                        if (!in_array($value, $acceptedValue))
                            throw new \Exception('La value "' . $value . '" pour l\'attribut "' . $attribute . '" n\'est pas acceptée');
                    }
                }
                // On termine par ajouter l'attribute à la chaine des attributes
                $attributesList .= ' ' . $attribute . '="' . htmlspecialchars($value, ENT_QUOTES, InitSystem::getCharset(), true) . '"';
            }
        }

        // Pour finir on génère la balise
        $output = '<' . $baliseName;
        if (!empty($attributesList))
            $output .= $attributesList;
        if (isset($baliseInfo['autoClose']) && $baliseInfo['autoClose'])
            $output .= '/';
        return $output . '>';
    }

    public function getEndBalise($baliseName) {
        if (!is_string($baliseName))
            throw new \Exception('Balise name must be a string');
        $baliseInfo = false;
        foreach ($this->_htmlData->baliseList->balise as $balise) {
            if ($balise->name == $baliseName) {
                $baliseInfo = $balise;
                break;
            }
        }
        if (!$baliseInfo)
            throw new \Exception('La balise "' . $baliseName . '" est invalid');

        if (isset($baliseInfo['autoClose']) && $baliseInfo['autoClose'])
            throw new \Exception('La balise "' . $baliseName . '" se ferme automatiquement lorsqu\'elle appelé');

        return '</' . (string) $baliseInfo->name . '>';
    }

    public function img($imgPath, $attributes = array()) {
        if (!file_exists($imgPath))
            throw new \Exception('L image "' . $imgPath . '" n existe pas');

        $attributes['src'] = (string) $imgPath;

        if (!isset($attributes['alt']))
            $attributes['alt'] = basename($imgPath);

        return $this->getBalise('img', $attributes);
    }

    public function link($linkPath, $title = null, $attributes = array()) {
        $attributes['href'] = (string) $linkPath;
        if ($title === null)
            $title = basename($linkPath);

        return $this->getBalise('a', $attributes) . $title . $this->getEndBalise('a');
    }

    public function getDoctype($htmlVersion = null) {
        if ($htmlVersion === null)
            $htmlVersion = $this->_htmlVersion;
        else
            $this->_checkHtmlVersion($htmlVersion);

        return '<!DOCTYPE HTML PUBLIC "-//W3C//DTD ' . $htmlVersion . '//EN" "' . $this->_htmlVersionList[$htmlVersion] . '">';
    }
    
    /*
     * Permet d'obtenir un head html...
     * 
     * @access public
     * @param void
     * @return string
     * 
     * @example : getHeadHtml('test', array('cache-control' => 'public'), array('abstract' => 'test'), array(array('type' =>'image/x-icon', 'href' => 'test.link'), array('href' => 'test.link')), array('type' => 'text/javascript', 'src'=> 'script.js'));
     */
    public function getHeadHtml($title, $metaHttpEquivList = array(), $metaNameList = array(), $linkList = array(), $scriptList = array()) {

        $head = $this->getBalise('head');
        if (!is_string($title))
            throw new \Exception('Title invalid');
        $head .= $this->getBalise('title') . $title . $this->getEndBalise('title');

        // MetaHttpEquiv
        if (!empty($metaHttpEquivList)) {
            foreach ($metaHttpEquivList as $httpEquivName => &$httpEquivContent) {
                if (!property_exists($this->_htmlData->metaHttpEquivList, $httpEquivName))
                    throw new \Exception('Meta Http Equiv "' . $httpEquivName . '" invalid');
                if (property_exists($this->_htmlData->metaHttpEquivList->$httpEquivName, 'acceptedValue')) {
                    $acceptedValue = explode(',', $this->_htmlData->metaHttpEquivList->$httpEquivName->acceptedValue);
                    if (!in_array($httpEquivContent, $acceptedValue))
                        throw new \Exception('Le content "' . $httpEquivContent . '" pour la balise meta http_equiv "' . $httpEquivName . '" n\'est pas valide');
                }
                $head .= $this->getBalise('meta', array('http-equiv' => $httpEquivName, 'content' => $httpEquivContent));
            }
        }
        //Meta name
        if (!empty($metaNameList)) {
            foreach ($metaNameList as $metaName => &$metaContent) {
                if (!property_exists($this->_htmlData->metaNameList, $metaName))
                    throw new \Exception('Meta Name "' . $metaName . '" invalid');
                if (property_exists($this->_htmlData->metaNameList->$metaName, 'acceptedValue')) {
                    $acceptedValue = explode(',', $this->_htmlData->metaNameList->$metaName->acceptedValue);
                    if (!in_array($metaContent, $acceptedValue))
                        throw new \Exception('La valeur "' . $metaContent . '" pour la balise meta name "' . $metaName . '" n\'est pas valide');
                }
                $head .= $this->getBalise('meta', array('name' => $metaName, 'content' => $metaContent));
            }
        }
        // $linkList
        if (!empty($linkList)) {
            foreach ($linkList as &$link) {
                $head .= ( !is_array($link)) ? $this->getBalise('link', $linkList) : $this->getBalise('link', $link);
                if (!is_array($link))
                    break;
            }
        }
        //$scriptList
        if (!empty($scriptList)) {
            foreach ($scriptList as &$script) {
                $head .= ( !is_array($script)) ? $this->getBalise('script', $scriptList) : $this->getBalise('script', $script);
                $head .= $this->getEndBalise('script');
                if (!is_array($script))
                    break;
            }
        }

        $head .= $this->getEndBalise('head');
        return $head;
    }
    public function indente($htmlString) {
        throw new \Exception('Indente Fonction no yet');
    }

}

?>
